﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Web;
using System.Web.Caching;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint.WebPartPages;
using Microsoft.SharePoint.Administration;

namespace Practical.SharePoint.Base.WebParts
{
    [Guid("7a560608-17e0-4396-bdfe-f301ad3b3d0d")]
    [ToolboxItemAttribute(false)]
    public class MySitesWebPart : DataFormWebPart
    {
        private const string _dvtFirstrow = "dvt_firstrow";
        private readonly string _objectCacheString = string.Format(CultureInfo.InvariantCulture,"{0}_{1}", SPContext.Current.Web.CurrentUser.LoginName, "MySites").ToUpperInvariant();
        private bool _disposed;
        
        /// <summary>
        /// Dispose the current object
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2215:Dispose methods should call base class dispose")]
        public sealed override void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            try
            {
                // Check to see if Dispose has already been called.
                if (!this._disposed)
                {
                    // If disposing equals true, dispose all managed
                    // and unmanaged resources.
                    if (disposing)
                    {
                        // Dispose managed resources.
                    }
                    // Note disposing has been done.
                    _disposed = true;
                }
            }
            finally
            {
                base.Dispose();
            }
        }

        protected override void SetDataSourceProperties()
        {
            base.SetDataSourceProperties();
            if (this.WebPartManager != null
                &&
                this.WebPartManager.DisplayMode == WebPartManager.BrowseDisplayMode)
            {
                XmlDocument xmlDocument = new XmlDocument();
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof (My));
                    xmlSerializer.Serialize(memoryStream, this.GetSitesData(SPContext.Current.Web.CurrentUser));
                    memoryStream.Seek(0, SeekOrigin.Begin);
                    xmlDocument.Load(memoryStream);
                }
                using (XmlDataSource xmlDataSource = new XmlDataSource())
                {
                    xmlDataSource.ID = "ds" + typeof (My);
                    xmlDataSource.EnableCaching = false;
                    xmlDataSource.Data = xmlDocument.OuterXml;
                    this.DataSource = xmlDataSource;
                    this.DataBind(true);
                }
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            if (this.WebPartManager != null
                &&
                this.WebPartManager.DisplayMode == WebPartManager.BrowseDisplayMode)
            {
                if (this.Page.IsPostBack && !this.Page.IsCallback)
                {
                    string eventArgs = this.Page.Request["__EVENTARGUMENT"];
                    if (string.Compare(eventArgs, 0, _dvtFirstrow, 0, _dvtFirstrow.Length, StringComparison.Ordinal) == 0)
                    {
                        string data;
                        if (this.DoesPostbackEqualEvent(eventArgs, _dvtFirstrow, true, out data))
                        {
                            if (this.ParameterValues.Collection.ContainsKey(_dvtFirstrow))
                            {
                                this.ParameterValues.Collection[_dvtFirstrow] = data;
                            }
                            else
                            {
                                this.ParameterValues.Collection.Add(_dvtFirstrow, data);
                            }
                        }
                    }
                }
            }
        }

        private My GetSitesData(SPUser spUser)
        {
            PrefixTrace.WriteLine("MySitesWebPart", "GetSiteData: Begin");
            My my = new My();
            try
            {
                PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: Check If Cache for {0} is null", _objectCacheString));
                if (HttpRuntime.Cache[_objectCacheString] == null)
                {
                    SPFarm thisFarm = SPFarm.Local;
                    SPWebService service = thisFarm.Services.GetValue <SPWebService>("");

                    PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture,"GetSiteData: Enumerating WebApplications"));
                    foreach (SPWebApplication webApplication in service.WebApplications)
                    {
                        PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: Enumerating Site Collections in {0}", webApplication.GetResponseUri(SPUrlZone.Default).AbsoluteUri));
                        foreach (string siteCollectionUrl in webApplication.Sites.Names)
                        {
                            string spSiteUrl = webApplication.GetResponseUri(SPUrlZone.Default).AbsoluteUri + siteCollectionUrl;
                            using (SPSite spSite = new SPSite(spSiteUrl))
                            {
                                SharePointSecurity.AnonymousSafeRunWithElevatedPrivileges
                                    (spSite, spSite.RootWeb,
                                     (spElevatedSite, spElevatedWeb) =>
                                     {
                                         spElevatedSite.CatchAccessDeniedException = false;
                                         try
                                         {
                                             PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: Enumerating Webs In Site Collection in {0}", spSiteUrl));
                                             foreach (SPWeb spWeb in spElevatedSite.AllWebs)
                                             {
                                                 MySite returnSite = GetPermissions(spUser, spWeb);
                                                 if (returnSite != null
                                                     &&
                                                     returnSite.Url != null
                                                     &&
                                                     returnSite.Title != null
                                                     )
                                                 {
                                                     PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture,"GetSiteData: Adding Active Site {0}", returnSite.Url));
                                                     my.MySites.Add(returnSite);
                                                 }
                                                 spWeb.Dispose();
                                             }
                                         }
                                         catch (SPException ex)
                                         {
                                             PrefixTrace.WriteLine(ex.ToString());
                                         }
                                         spElevatedSite.CatchAccessDeniedException = true;
                                     });
                            }
                        }
                    }

                    AddMySitesToCache(my);
                }
                else
                {
                    using (new SPMonitoredScope("Reading From Cache"))
                    {
                        PrefixTrace.WriteLine("Begin Reading From Cache");
                        PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: Begin Reading From Cache"));
                        my = (My)HttpRuntime.Cache[_objectCacheString];
                        PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: Cache Value: {0}",my));
                        PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetSiteData: End Reading From Cache"));
                       
                    }
                }
            }
            catch (SPException ex)
            {
                HandleException(ex);
            }
            PrefixTrace.WriteLine("MySitesWebPart", "GetSiteData: End");
            my.Sort();
            return my;
        }

        private void AddMySitesToCache(My my)
        {
            using (
                new SPMonitoredScope(string.Format(CultureInfo.CurrentCulture, "Page_Load: Adding To Cache For {0} Seconds",
                                                   this.PermissionsCacheTime)))
            {
                HttpRuntime.Cache.Add(
                    _objectCacheString,
                    my,
                    null,
                    DateTime.UtcNow.AddSeconds(this.PermissionsCacheTime),
                    Cache.NoSlidingExpiration,
                    CacheItemPriority.High,
                    null);
            }
        }

        private static MySite GetPermissions(SPUser spUser, SPSecurableObject spSecurableObject)
        {
            PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture,"GetPermissions: Begin"));
            MySite mySite = new MySite();
            SPWeb spWeb = spSecurableObject as SPWeb;
            if (spWeb == null)
            {
                return mySite;
            }
            using (new SPMonitoredScope(string.Format(CultureInfo.CurrentCulture, "GetPermissions on {0}", spWeb.Url)))
            {
                PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetPermissions: Try {0}", spWeb.Url));
                try
                {
                    if (
                        (spWeb.AssociatedOwnerGroup != null && spWeb.AssociatedOwnerGroup.IsUserInGroup(spUser))
                        ||
                        (spWeb.AssociatedMemberGroup != null && spWeb.AssociatedMemberGroup.IsUserInGroup(spUser))
                       )
                    {
                        mySite.Id = spWeb.ID;
                        mySite.Url = spWeb.Url;
                        mySite.Title = spWeb.Title;
                    }
                }
                catch (SPException ex)
                {
                    PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "Exception: {0}", ex));
                }
                PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetPermissions: End Try"));
            }
            PrefixTrace.WriteLine("MySitesWebPart", string.Format(CultureInfo.CurrentCulture, "GetPermissions: End"));
            return mySite;
        }

        private void HandleException(Exception exception)
        {
            this.Controls.Clear();
            this.Controls.Add(new LiteralControl("ERROR: " + exception.Message));
            Debug.WriteLine(exception.Message);
            Debug.WriteLine(exception.StackTrace);
#if DEBUG
            this.Controls.Add(new LiteralControl("STACKTRACE: " + exception.StackTrace));
            while (exception.InnerException != null)
            {
                Debug.WriteLine(exception.InnerException);
            }
#endif
        }

        #region Web Part Properties
        [WebDisplayName("Item Row Limit")]
        [WebDescription("Number Of Items To Show Per Page")]
        [Category("Settings")]
        [DefaultValue(5)]
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        public int ItemRowLimit { get; set; }

        [WebDisplayName("Permissions Query Cache Time (In Seconds)")]
        [WebDescription("How Long To Cache Permission Query Results")]
        [Category("Settings")]
        [DefaultValue(0)]
        [Personalizable(PersonalizationScope.Shared)]
        [WebBrowsable(true)]
        public int PermissionsCacheTime { get; set; }
        #endregion
    }
}
